package server

import (
	"errors"
	"log"
	"sort"
	"time"
)

type Dealer struct {
	Player      []*Player
	PlayerMap   map[string]*Player
	playerIndex int //玩家排序值
	Button      int

	cards []Card //洗好的牌
	index int    //发牌index

	Playing   bool    //是否开始
	Turn      int     //出牌palyer
	CommCards [5]Card //池底的牌
	BetTurn   int     //下注round
	LastBet   int     //上次下注
	Totalbet  int     //池底
}

func (d *Dealer) AddPlayer(p *Player) error {
	if d.Playing {
		readyRes := Response{
			Type: "err",
			Msg:  "game was started",
		}
		p.Conn.WriteJSON(readyRes)
		return errors.New("game was started")
	}

	mapLen := len(d.PlayerMap)
	if mapLen > 8 {
		readyRes := Response{
			Type: "err",
			Msg:  "too many player in the room",
		}
		p.Conn.WriteJSON(readyRes)
		return errors.New("too many player in the room")
	}

	p.Index = d.playerIndex
	d.playerIndex++

	res := Response{
		Type: "online",
		Msg: ResPlayerAdd{
			Name:  p.Name,
			Index: p.Index,
		},
	}

	otherOnline := Response{
		Type: "online",
	}

	//通知其他人有人上线
	for _, v := range d.PlayerMap {
		log.Println("notify", v.Name, p.Name)
		//
		err := v.Conn.WriteJSON(res)
		if err != nil {
			log.Println("send user online error ", v.Name)
			continue
		}

		//通知自己，其它在线的用户
		otherOnline.Msg = ResPlayerAdd{
			Name:  v.Name,
			Index: v.Index,
		}
		err = p.Conn.WriteJSON(otherOnline)
		if err != nil {
			log.Println("send user online error ", v.Name)
			continue
		}
	}
	//通知自己
	p.Conn.WriteJSON(res)

	d.PlayerMap[p.Name] = p
	d.Player = append(d.Player, p)
	log.Println("addPlayer")

	return nil
}

func (d *Dealer) RemovePlayer(p *Player) error {
	if len(d.PlayerMap) < 1 {
		return errors.New("no player in the room")
	}

	var deleteIndex = -1
	for i := 0; i < len(d.Player); i++ {
		if d.Player[i] == p {
			deleteIndex = i
			break
		}
	}
	if deleteIndex > -1 {
		d.Player = append(d.Player[:deleteIndex], d.Player[deleteIndex+1:]...)
	}
	delete(d.PlayerMap, p.Name)
	log.Println("player len:", len(d.Player), "cap:", cap(d.Player))
	//所有人离开游戏结束
	if len(d.PlayerMap) < 1 {
		d.reset()
	}

	offline := Response{
		Type: "offline",
		Msg: ResPlayerAdd{
			Name:  p.Name,
			Index: p.Index,
		},
	}

	for _, p := range d.PlayerMap {
		p.Conn.WriteJSON(offline)
	}

	return nil
}

func (d *Dealer) ReadyPlayer(p *Player) error {
	if d.Playing {
		readyRes := Response{
			Type: "err",
			Msg:  "is playing",
		}
		p.Conn.WriteJSON(readyRes)
		return errors.New("is playing")
	}
	if d.Totalbet < 10 {
		readyRes := Response{
			Type: "err",
			Msg:  "没有足够筹码",
		}
		p.SendMsg(readyRes)
		return errors.New("no enough chips")
	}

	p.Ready = !p.Ready

	readyRes := Response{
		Type: "ready",
		Msg: ResPlayerReady{
			Name:  p.Name,
			Ready: p.Ready,
			Total: p.Total,
		},
	}
	for _, v := range d.PlayerMap {
		v.Conn.WriteJSON(readyRes)
	}

	//检查是否所有人都准备
	var allReady = true
	for _, v := range d.PlayerMap {
		if !v.Ready {
			allReady = false
			break
		}
	}
	log.Println("all ready", allReady)
	if allReady { //所有人准备
		d.Playing = true
		//大盲下家
		d.Turn = (d.Button + 3) % len(d.Player)
		d.allReady(d.Player[d.Turn].Name, d.LastBet) //确定button smallblind bigblind
	}

	return nil
}

func (d *Dealer) allReady(name string, bet int) {
	btn := d.Player[d.Button%len(d.Player)]
	sb := d.Player[(d.Button+1)%len(d.Player)]
	bb := d.Player[(d.Button+2)%len(d.Player)]

	sb.bet(d.BetTurn, bet/2)
	bb.bet(d.BetTurn, bet)

	d.cards = Shuffle()
	log.Println("HoleCard...")
	for i := 0; i < 2; i++ {
		start := d.Button + 1
		length := len(d.Player)
		for m := 0; m < length; m++ {
			player := d.Player[start%length]
			start++
			cardIndex := d.index % len(d.cards)
			player.Hole[i] = d.cards[cardIndex]
			d.index ++ //下一张牌
		}
	}

	res := Response{
		Type: "allready",
	}
	allready := AllReady{
		Btn:  btn.Name,
		Sb:   sb.Name,
		Bb:   bb.Name,
		Bet:  bet,
		Turn: name,
	}
	for _, p := range d.PlayerMap {
		allready.Cards = p.Hole[:]
		res.Msg = allready
		p.SendMsg(res)
	}
}

func (d *Dealer) NextRound() {
	d.poolCard()
	d.BetTurn++

	d.LastBet = 0
	d.Turn = d.getSmallIndex()

	log.Println("betTurn:", d.BetTurn)
}

func (d *Dealer) poolCard() {
	var cards []Card
	cardLen := len(d.cards)

	switch d.BetTurn {
	case 0:
		for i := 0; i < 3; i++ {
			d.CommCards[i] = d.cards[d.index%cardLen]
			d.index ++
		}
		cards = d.CommCards[:3]
	case 1:
		d.index ++
		d.CommCards[3] = d.cards[d.index%cardLen]
		cards = d.CommCards[3:4]
	case 2:
		d.index += 2
		d.CommCards[4] = d.cards[d.index%cardLen]
		cards = d.CommCards[4:]
	}

	res := Response{
		Type: "poolcard",
		Msg: PoolCard{
			Cards:   cards,
			Turn:    d.getSmallBlind().Name,
			Pot:     d.Totalbet,
			BetTurn: d.BetTurn,
		},
	}
	for _, player := range d.PlayerMap {
		player.Conn.WriteJSON(res)
	}
}

func (d *Dealer) allInRound() {
	for {
		d.NextRound()
		if d.BetTurn >= 3 {
			break
		}
		time.Sleep(500 * time.Millisecond)
	}
	time.Sleep(time.Second)
}

func (d *Dealer) Showdown() {
	log.Println("showdown")
	winner := make([]*Player, 0, 9)
	log.Println("make winner")
	//牌大小排序
	showdownSlice := make(ShowdownSlice, 0, 7)
	log.Println("make showdownSlice")

	for _, p := range d.Player {
		allCards := append(p.Hole[:], d.CommCards[:]...)
		log.Println("allCards:", allCards)
		n, resultCard := Pokervalue(allCards)
		s := Showdown{
			Type:  int(n),
			Cards: resultCard,
			P:     p,
		}
		showdownSlice = append(showdownSlice, s)
	}
	sort.Sort(showdownSlice)
	log.Println("showdownSlice: ", showdownSlice)

	var cardType int
	l := len(showdownSlice)
	for i := 0; i < l; i++ {
		if showdownSlice[i].P.isFold() {
			continue
		}
		winner = append(winner, showdownSlice[i].P)
		cardType = showdownSlice[i].Type

		//计算相同牌型
		for j := i + 1; j < l; j++ {
			if showdownSlice[j].P.isFold() {
				continue
			}
			if showdownSlice[i].Type == showdownSlice[j].Type {
				var flag = true
				for in := 0; in < len(showdownSlice[i].Cards); in++ {
					if showdownSlice[i].Cards[in].Number != showdownSlice[j].Cards[in].Number {
						flag = false
						break
					}
				}
				if flag {
					winner = append(winner, showdownSlice[j].P)
				}
			} else {
				break
			}
		}
		break
	}

	log.Println("winner", winner)
	//分筹码


	//打印winner名字
	var winnerNames = make([]string, 0)
	for _, v := range winner {
		winnerNames = append(winnerNames, v.Name)
	}
	winRes := Response{
		Type: "showdown",
		Msg: ShowDown{
			Names:    winnerNames,
			CardType: getCardType(cardType),
		},
	}
	for _, p := range d.Player {
		p.SendMsg(winRes)
	}
	//重新开始
	d.reset()

}

//-1 fold, 0 check, 1 raise, 2 allin
func (d *Dealer) PlayerAction(p *Player, bet int) {
	if d.getTurnPlayer().Name != p.Name {
		log.Println("not your turn", p.Name, "it is ", d.getTurnPlayer().Name)
		return
	}

	log.Println("playerAction:", p.Name, bet)
	msg := BetAndNext{
		Name: p.Name, //本次action玩家
	}

	if -1 == bet { //fold
		msg.Type = -1
		p.Fold = true
	} else {
		//
		if p.Total > bet {
			if 0 == bet {
				msg.Type = 0
				msg.Bet = 0
				p.bet(d.BetTurn, bet)
			} else if d.LastBet == bet { //call
				msg.Type = 3
				msg.Bet = bet
				p.bet(d.BetTurn, bet)
			} else if d.LastBet < bet { //raise
				msg.Type = 1
				d.LastBet = bet
				msg.Bet = bet
				p.bet(d.BetTurn, bet)
			} else {
				log.Println("bet:", bet)
				p.SendMsg(Response{"err", "下注不符合规则"})
				return
			}
			d.Totalbet += bet //加入池底
		} else { //all-in
			log.Println(p.Name, p.Total, "all-in")
			if p.Total > d.LastBet {
				d.LastBet = p.Total
			}
			d.Totalbet += p.Total //加入池底
			msg.Type = 2
			msg.Bet = p.allIn(d.BetTurn)
			log.Println("bet", msg.Bet)
		}
	}

	msg.Total = p.Total //还剩筹码

	//检查是否下注完毕
	var nextPlayer *Player = nil
	pLen := len(d.Player)
	var allInNum = 0
	var foldNum = 0
	for i := 0; i < pLen; i++ {
		d.Turn++
		p := d.Player[d.Turn%pLen]
		if p.isFold() {
			foldNum++
			continue
		}
		if p.isAllin() {
			allInNum++
			continue
		}

		if d.BetTurn == 0 {
			if d.Turn-d.Button-3 < pLen {
				nextPlayer = p
				break
			}
		} else {
			if d.Turn-d.Button-1 < pLen {
				log.Println("lastBet", d.LastBet, "name:", p.Name, "turn:", d.Turn)
				nextPlayer = p
				break
			}
		}

		if d.LastBet != p.Bet[d.BetTurn] {
			log.Println("lastBet", d.LastBet, "name:", p.Name, "turn:", d.Turn)
			nextPlayer = p
			break
		}

	}

	if nil == nextPlayer {
		msg.Turn = ""
	} else { //下一人下注
		msg.Turn = nextPlayer.Name
		log.Println("nextPlayer:", msg.Turn)
	}
	res := Response{
		Type: "betAndNext",
		Msg:  msg,
	}
	for _, v := range d.PlayerMap {
		v.SendMsg(res)
	}

	//本轮结束
	if nil == nextPlayer {
		log.Println("this turn over allin:", allInNum, "fold:", foldNum)
		log.Println("player action, betTurn", d.BetTurn)
		//checkResult
		if pLen == allInNum+foldNum {
			d.allInRound()
			d.Showdown()
		} else if pLen == foldNum+1 {
			for _, p := range d.Player {
				if !p.isFold() {
					var winnerNames = make([]string, 0)
					winnerNames = append(winnerNames, p.Name)
					winRes := Response{
						Type: "showdown",
						Msg: ShowDown{
							Names:    winnerNames,
							CardType: "",
						},
					}
					for _, p := range d.Player {
						p.SendMsg(winRes)
					}
					break
				}
			}
		} else if 3 == d.BetTurn {
			d.Showdown()
		} else {
			//发牌
			d.NextRound()
		}
	}

}

func getCardType(t int) string {
	var cardType = ""
	switch t {
	case Highcard:
		cardType = "Highcard"
	case Pair: // 1
		cardType = "Pair"
	case TwoPairs: // 2
		cardType = "TwoPairs"
	case ThreeofAKind: // 3
		cardType = "ThreeofAKind"
	case Straight: // 4
		cardType = "Straight"
	case xFlush: // 5
		cardType = "xFlush"
	case Fullhouse: // 6
		cardType = "Fullhouse"
	case FourofAKind: // 7
		cardType = "FourofAKind"
	case StraightFlush: // 8
		cardType = "StraightFlush"
	case RoyalFlush: // 9
		cardType = "RoyalFlush"

	}
	return cardType
}

//=========================
func (d *Dealer) getSmallBlind() *Player {
	return d.Player[(d.Button+1)%len(d.Player)]
}

func (d *Dealer) getSmallIndex() int {
	var index = 0
	sb := d.getSmallBlind()
	for i, p := range d.Player {
		if p.Name == sb.Name {
			index = i + len(d.Player) // turn>player.length
			break
		}
	}
	return index
}

func (d *Dealer) getTurnPlayer() *Player {
	return d.Player[d.Turn%len(d.Player)]
}

func (d *Dealer) reset() {
	d.Button += 1
	d.Playing = false
	d.Turn = 0
	d.cards = make([]Card, 0)
	d.CommCards = [5]Card{}
	d.BetTurn = 0
	d.LastBet = 0
	d.Totalbet = 10
	d.index++
	for _, p := range d.Player {
		p.Ready = false
	}

}
